package game.Rendering;

import game.Configuration;
import game.Helper;
import game.Player.Input;
import game.Player.Player;
import game.MainMenu;
import game.Sprites.SpriteResources;
import game.Sprites.SpriteSheet;
import game.Utils.AnimatedDouble;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;

public class GameCanvas extends Application {
    protected double unizarSplashTime = 5.0;
    protected double copyrightTime = 4.0;
    protected AnimatedDouble splash1Anim = new AnimatedDouble(1.0, 0.0, unizarSplashTime, true);
    protected boolean splash1Shown;
    protected AnimatedDouble splash2Anim = new AnimatedDouble(1.0, 0.0, copyrightTime, true);
    protected boolean splashShown;

    Image splashOne, splashTwo;

    protected static Canvas instance;
    protected static Stage stageInstance;
    protected static Pane root;
    protected static game.Scene mainScene;
    public static Canvas getInstance(){
        return instance;
    }
    public static Stage getStage(){
        return stageInstance;
    }

    public static void main(String[] args)
    {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage) {
        splash1Shown = splashShown = false;
        splash1Anim.reset();
        try {
            splashOne = SwingFXUtils.toFXImage(ImageIO.read(SpriteResources.class.getClassLoader().getResourceAsStream("splash/unizar_valve.png")), null);
            splashTwo = SwingFXUtils.toFXImage(ImageIO.read(SpriteResources.class.getClassLoader().getResourceAsStream("splash/splash_copyright.png")), null);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Create the Canvas
        Canvas canvas = new Canvas(1280, 720);
        instance = canvas;
        stageInstance=stage;

        Input input = new Input();

        stage.setWidth(1280);
        stage.setHeight(720);
        stage.getIcons().add(new Image(getClass().getClassLoader().getResourceAsStream("icon.png")));


        // Set width and height of the Canvas
        GameCanvas.getInstance().setWidth(getStage().getWidth());
        GameCanvas.getInstance().setHeight(getStage().getHeight());

        // Get the graphics context of the canvas
        GraphicsContext gc = canvas.getGraphicsContext2D();

        // Create the Pane
        root = new Pane();

        // Set the Style-properties of the Pane
       /* root.setStyle("-fx-padding: 10;" +
                "-fx-border-style: solid inside;" +
                "-fx-border-width: 2;" +
                "-fx-border-insets: 5;" +
                "-fx-border-radius: 5;" +
                "-fx-border-color: blue;");*/

        // Add the Canvas to the Pane
        root.getChildren().add(canvas);

        // Create the Scene
        Scene scene = new Scene(root);

        // Bind keys and mouse
        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                input.keyPressed(event);
            }
        });
        scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                input.keyReleased(event);
            }
        });

        scene.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                input.mousePressed(mouseEvent);
            }
        });
        scene.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                input.mouseClicked(mouseEvent);
            }
        });
        scene.setOnMouseReleased(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                input.mouseReleased(mouseEvent);
            }
        });

        // Set cursos
        //TODO: Revisar
        BufferedImage img = null;
        try {
            img = ImageIO.read(getClass().getClassLoader().getResourceAsStream("Sprites/hud/pointer.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            SpriteSheet pointers = new SpriteSheet(img, 2, 2);
            Toolkit kit = Toolkit.getDefaultToolkit();
            Image image = pointers.get(0);  //pass in the image path
            Cursor cursor = new ImageCursor(image, image.getWidth()/2,image.getHeight()/2);
            scene.setCursor(cursor);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Add the Scene to the Stage
        stage.setScene(scene);

        // Set the Title of the Stage
        stage.setTitle("CS2D");
        stage.setResizable(false);
        stage.setFullScreen(true);
        stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);

        // RENDER CANVAS FRAMES
        try {
            new AnimationTimer()
            {
                Configuration conf = new Configuration();
                MainMenu mainMenu = new MainMenu(gc);
                long lastTime = 0L;
                public void handle(long currentNanoTime)
                {
                    // Clear the canvas
                    long nanosElapsed = currentNanoTime - lastTime;
                    long millisElapsed = nanosElapsed / 1000000L;
                    lastTime = currentNanoTime;

                    int FPS = (int)(1.0 / (millisElapsed / 1000.0));

                    long startTime = System.nanoTime();
                    draw(gc, mainMenu, millisElapsed);

                    long startUpdateTime = System.nanoTime();
                    update(mainMenu, millisElapsed * 1e-3);

                    long endUpdateTime = System.nanoTime();

                    long millistUpdate = (endUpdateTime - startUpdateTime)/1000000L;
                    long millistDraw = (startUpdateTime - startTime)/1000000L;


                    //gc.setFill( Color.BLUE );
                    //gc.fillText(String.valueOf(FPS), 360, 36 );
                    //gc.strokeText(String.valueOf(FPS), 360, 36 );
                }
            }.start();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Display the Stage
        stage.show();
    }

    /**
     * Called each frame
     * @param deltaTime time passed in seconds
     */
    private void update(MainMenu mainMenu, double deltaTime) {

        // Set Config resolution
        double resWidth = Double.parseDouble(Configuration.getResolution().split("x")[0]);
        double resHeight = Double.parseDouble(Configuration.getResolution().split("x")[1]);

        // Only if not fullscreen and not maximized
        if(!getStage().isFullScreen() && !getStage().isMaximized()){
            if(getStage().getWidth() != resWidth || getStage().getHeight() != resHeight){
                getStage().setWidth(resWidth);
                getStage().setHeight(resHeight);
            }
        }else{
            // Is fullscreen or maximized
        }

        // Set config fullscreen
        if(Configuration.getFullscreen() != getStage().isFullScreen()){
            getStage().setFullScreen(Configuration.getFullscreen());
        }

        double targetWidth = getStage().getWidth();
        double targetHeight = getStage().getHeight();
        if(!getStage().isFullScreen()){
            targetWidth-=4;
            targetHeight-=28;
        }


        if(getInstance().getWidth() != targetWidth ||
                getInstance().getHeight() != targetHeight){

            GameCanvas.getInstance().setWidth(targetWidth);
            GameCanvas.getInstance().setHeight(targetHeight);
        }
        Input.update();
        if(Helper.mainScene != null) {
            Helper.mainScene.update(deltaTime);
        }else{
            mainMenu.update(deltaTime);
        }

    }

    /**
     * Called each grame
     * @param gc
     */
    private void draw(GraphicsContext gc, MainMenu mainMenu, long millisElapsed) {
        if (Helper.mainScene != null && Helper.mainScene.getCamera() != null) {
            Helper.mainScene.getCamera().draw(gc);
        } else {
            if(splash1Shown) {
                gc.setGlobalAlpha(1.0);
                mainMenu.draw(gc);
            }
            if(!splashShown){
                int screenWidth = (int) GameCanvas.getInstance().getWidth();
                int screenHeight = (int) GameCanvas.getInstance().getHeight();
                if(splash1Anim.getValue() > 0.0){
                    gc.setGlobalAlpha(1.0);
                    gc.setFill(Color.BLACK);
                    gc.fillRect(0,0,screenWidth, screenHeight);
                    gc.setGlobalAlpha(Math.min(splash1Anim.getValue() * unizarSplashTime, 1.0));
                    gc.drawImage(splashOne, screenWidth/2 - splashOne.getWidth()/2,
                            screenHeight/2 - splashOne.getHeight()/2);
                }else if(!splash1Shown){
                    splash1Shown = true;
                    splash2Anim.reset();
                }

                else if(splash1Shown && splash2Anim.getValue() > 0.0){
                    gc.setFill(Color.BLACK);
                    gc.setGlobalAlpha(Math.min(splash2Anim.getValue() * copyrightTime, 1.0));
                    gc.fillRect(0,0,screenWidth, screenHeight);
                    gc.drawImage(splashTwo, screenWidth/2 - splashTwo.getWidth()/2,
                            screenHeight/2 - splashTwo.getHeight()/2);
                }
                else{
                    System.out.println("Welcome to CS2D, have fun!");
                    splashShown = true;
                }
            }
        }

        int FPS = (int)(1.0 / (millisElapsed / 1000.0));
    }

    public static void setMainScene(game.Scene scene){
        mainScene = scene;
    }

    public static Pane getPane(){
        return root;
    }
}
